using System;
using System.Collections;
using System.Collections.Generic;
using System.Web.UI;
using System.Web.UI.WebControls;

using BizElements.BusinessLayer;
using BizElements.Web;

namespace BizElements.Web.UI
{
    /// <summary>Form opened by <b>SearchFrm</b> that allow user's to set their own search preferences. Shares settings data with SearchFrm via sessions.</summary>
    public partial class SearchOptionsFrm : System.Web.UI.Page
    {
        /// <summary>Initialize Page.UICulture to match culture defined in UserSession.UiCulture.</summary>
        protected override void InitializeCulture()
        {
            this.UICulture = UserSession.UiCulture.Name;
        }

        #region Bindings.

        BusinessObjectsGridAdapter uiSettingsAdapter;

        protected override void OnInit(EventArgs e)
        {
            base.OnInit(e);
            BindDisplayFields();
        }

        private void BindDisplayFields()
        {
            this.uiDisplayFieldSettings.DataKeyField = "Field";
            this.uiDisplayFieldSettings.BindField(new BindableField("Caption", this.uiFieldCaption.Text, typeof(string)), 200);
            IBindableField hidden = new BindableField("IsHidden", this.uiHiddenCaption.Text, typeof(bool));
            this.uiDisplayFieldSettings.BindField(hidden, new BoundCheckBoxTemplate(hidden, GetFormatter()), true);
            IBindableField ordinal = new BindableField("Ordinal", this.uiOrdinalCaption.Text, typeof(int));
            this.uiDisplayFieldSettings.BindField(ordinal, new BoundTextBoxTemplate(ordinal, GetFormatter(), 30, null, null, 2, "0123456789"), true);
        }

        #endregion

        protected void Page_Load(object sender, EventArgs e)
        {
            if (!IsPostBack)
            {
                BindVisibleSortFields();
                DisplaySettings();
            }

            if (this.uiSettingsAdapter == null)
                this.uiSettingsAdapter = new BusinessObjectsGridAdapter(this.uiDisplayFieldSettings, this.EditedSettings);
        }

        private void BindVisibleSortFields()
        {
            this.uiDefaultSort.Width = new Unit("200px");
            var visibleFields = ArrayUtil.FindAll<List<IBindableField>, IBindableField>(this.Engine.DisplayFields, (f) => !f.IsHidden);
            this.uiDefaultSort.BindDataSource(visibleFields, "BindingName", "Caption", true);
        }

        #region Fetch and display settings.

        private void DisplaySettings()
        {
            // Display fields.
            List<DisplayFieldSettings> allSettings = new List<DisplayFieldSettings>();
            foreach (IBindableField displayField in this.Engine.DisplayFields)
            {
                bool isHiddenByUnderlyingSystem = displayField.IsHidden;
                if (!isHiddenByUnderlyingSystem)
                {
                    DisplayFieldSettings newOrExisting = new DisplayFieldSettings(this.ProviderKey, displayField.BindingName, displayField.Caption);
                    newOrExisting.Fetch(GetActor(), DataAccessScope.AllRelatedEntites, false);
                    allSettings.Add(newOrExisting);
                }
            }

            this.EditedSettings = allSettings;
            this.uiSettingsAdapter = new BusinessObjectsGridAdapter(this.uiDisplayFieldSettings, this.EditedSettings);
            this.uiSettingsAdapter.DataBind();
            this.uiSettingsAdapter.EditAll();

            // Default sort by.
            DefaultSortSetting sortSetting = new DefaultSortSetting(this.ProviderKey, null);
            sortSetting.Fetch(GetActor(), DataAccessScope.AllRelatedEntites, false);
            this.uiDefaultSort.TrySelectValue(sortSetting.Field);
        }

        #endregion

        #region Getters, sesssion/relativeFormUrl properties.

        private string ProviderKey
        {
            get { return Request.QueryString["providerKey"]; }
        }

        private static IActor GetActor()
        {
            return UserSession.Actor;
        }

        private static IMessageFormatter GetFormatter()
        {
            return UserSession.Formatter;
        }

        ISearchEngine engine;

        private ISearchEngine Engine
        {
            get
            {
                if (this.engine == null)
                {
                    SearchProvider provider = SearchProviderRegistry.GetInstance().GetProvider(this.ProviderKey);
                    this.engine = provider.CreateEngineBuilderInstance().CreateSearchEngine(GetActor());
                }

                return this.engine;
            }
        }

        private List<DisplayFieldSettings> EditedSettings
        {
            get { return Session["SearchOptionsFrm.EditedSettings"] as List<DisplayFieldSettings>; }
            set { Session["SearchOptionsFrm.EditedSettings"] = value; }
        }

        #endregion

        #region ResetAllSettings.

        private void ResetAllSettings()
        {
            // Display fields.
            foreach (DisplayFieldSettings fieldSettings in this.EditedSettings)
            {
                fieldSettings.IsHidden = false;
                fieldSettings.Ordinal = null;
            }

            this.uiSettingsAdapter.EditAll();

            // Default sort.
            this.uiDefaultSort.SelectedIndex = -1;
        }

        protected void uiResetAll_Click(object sender, EventArgs e)
        {
            ResetAllSettings();
        }

        #endregion

        #region Save.

        protected void uiSave_Click(object sender, EventArgs e)
        {
            SaveSettings();
            Close(true);
        }

        private void SaveSettings()
        {
            // Display fields.
            this.uiSettingsAdapter.AcceptChanges();
            foreach (DisplayFieldSettings fieldSettings in this.EditedSettings)
                fieldSettings.Save(GetActor(), false);

            // Default sort.
            string dfltSortField = TextUtil.TrimNullableString(this.uiDefaultSort.SelectedValue);
            DefaultSortSetting sortSetting = new DefaultSortSetting(this.ProviderKey, dfltSortField);
            sortSetting.Save(GetActor(), false);
        }

        private void Close(bool refreshSearchForm)
        {
            this.EditedSettings = null;
            if (refreshSearchForm)
                ScriptManager.RegisterStartupScript(this, GetType(), "RefreshSearchForm", "try{window.opener.Refresh();}catch(e){} window.close(); ", true);
            else
                JScript.CloseWindow();
        }

        #endregion

        private sealed class DisplayFieldSettings : ISelfValidatingObject
        {
            #region Constants.

            const string module = "ASP.common_searchfrm_aspx";
            const string engineComponent = "engine";
            const string isHiddenPath = "DisplayFields/{0}/IsHidden";
            const string ordinalPath = "DisplayFields/{0}/Ordinal";

            #endregion

            #region FieldPermissions.

            string context;
            string field;
            string caption;

            bool isHidden;
            int ordinal;

            #endregion

            #region CTor.

            public DisplayFieldSettings(string context, string field, string caption)
            {
                this.context = context;
                this.field = field;
                this.caption = caption;
            }

            #endregion

            #region ISelfValidatingObject Members

            public ITextSource MessageSource
            {
                get { return null; }
                set { }
            }

            public BusinessRuleCollection GrantFetch(IActor actor)
            {
                return new BusinessRuleCollection();
            }

            public BusinessRuleCollection GrantSave(IActor actor)
            {
                return new BusinessRuleCollection();
            }

            public BusinessRuleCollection Validate(ValidationLevel level)
            {
                return new BusinessRuleCollection();
            }

            public bool Fetch(IActor actionInitiator, DataAccessScope detailLevel, bool enforceSecurityChecks)
            {
                IList<IAppSetting> engineSettings = SettingsUtil.GetComponentSettings(module, this.context, engineComponent);

                string cfgHidden = GetPropertySetting(engineSettings, string.Format(isHiddenPath, this.field));
                try { this.isHidden = (cfgHidden != null) ? bool.Parse(cfgHidden) : false; }
                catch { this.isHidden = false; }

                string cfgOrdinal = GetPropertySetting(engineSettings, string.Format(ordinalPath, this.field));
                try { this.ordinal = (cfgOrdinal != null) ? int.Parse(cfgOrdinal) : 0; }
                catch { this.ordinal = 0; }

                bool settingsExists = (engineSettings.Count > 0);
                return settingsExists;
            }

            private static string GetPropertySetting(IList<IAppSetting> allSettings, string propertyPathString)
            {
                IAppSetting setting = ArrayUtil.GetObjectByKey<IAppSetting, string>(allSettings, "PropertyPathString", propertyPathString);
                string value = (setting != null && setting.Value != null) ? Convert.ToString(setting.Value) : null;
                if (value == "")
                    value = null;

                return value;
            }

            public void Save(IActor actionInitiator, bool enforceSecurityChecks)
            {
                if (this.isHidden == false)
                    SettingsUtil.Manager.Delete(CreateFilter(engineComponent, string.Format(isHiddenPath, this.field)));
                else
                    SettingsUtil.SaveUserSetting(module, this.context, engineComponent, string.Format(isHiddenPath, this.field), this.isHidden);

                if (this.ordinal == 0)
                    SettingsUtil.Manager.Delete(CreateFilter(engineComponent, string.Format(ordinalPath, this.field)));
                else
                    SettingsUtil.SaveUserSetting(module, this.context, engineComponent, string.Format(ordinalPath, this.field), this.ordinal);
            }

            private IAppSetting CreateFilter(string component, string propertyPathString)
            {
                IAppSetting filter = SettingsUtil.Manager.NewSetting();
                filter.ActorId = SettingsUtil.ActorProvider().Id;
                filter.Application = SettingsUtil.CurrentApplication;
                filter.Component = component;
                filter.Context = this.context;
                filter.Module = module;
                filter.PropertyPathString = propertyPathString;

                return filter;
            }

            #endregion

            #region IValueObject Members

            public bool IsReadOnly
            {
                get { return false; }
                set { }
            }

            public void SetPropertyValue(string propertyName, object val)
            {
                ReflectionUtil.TrySetPropertyValue(this, propertyName, val, true);
            }

            #endregion

            #region Properties.

            public string Field
            {
                get { return this.field; }
                set { }
            }

            public string Caption
            {
                get { return this.caption; }
                set { }
            }

            public bool IsHidden
            {
                get { return isHidden; }
                set { isHidden = value; }
            }

            public int? Ordinal
            {
                get { return (this.ordinal > 0) ? (int?)this.ordinal : null; }
                set { this.ordinal = (value != null && value > 0) ? value.Value : 0; }
            }

            #endregion

            string ICaptionProvider.GetFieldCaption(string propertyName)
            {
                return null;
            }
        }

        private sealed class DefaultSortSetting
        {
            #region Constants.

            const string Module = "ASP.common_searchfrm_aspx";
            const string DefaultSortComponent = "defaultSortBy";

            #endregion

            #region CTor.

            public DefaultSortSetting(string context, string field)
            {
                this.context = context;
                this.field = field;
            }

            #endregion

            #region Properties.

            string context;

            public string Context
            {
                get { return this.context; }
                set { this.context = value; }
            }

            string field;

            public string Field
            {
                get { return this.field; }
                set { this.field = value; }
            }

            #endregion

            public bool Fetch(IActor user, DataAccessScope detailLevel, bool enforceSecurityChecks)
            {
                IList<IAppSetting> sortSettings = SettingsUtil.GetComponentSettings(Module, this.context, DefaultSortComponent);
                bool exists = (sortSettings != null && sortSettings.Count > 0);
                if (exists)
                {
                    this.field = Convert.ToString(sortSettings[0].Value);
                    this.field = TextUtil.TrimNullableString(this.field);
                }
                else
                {
                    this.field = null;
                }

                return exists;
            }

            public void Save(IActor actionInitiator, bool enforceSecurityChecks)
            {
                if (string.IsNullOrEmpty(this.field))
                    SettingsUtil.Manager.Delete(CreateFilter(DefaultSortComponent));
                else
                    SettingsUtil.SaveUserSetting(Module, this.context, DefaultSortComponent, null, this.field);
            }

            private IAppSetting CreateFilter(string component)
            {
                IAppSetting filter = SettingsUtil.Manager.NewSetting();
                filter.ActorId = SettingsUtil.ActorProvider().Id;
                filter.Application = SettingsUtil.CurrentApplication;
                filter.Component = component;
                filter.Context = this.context;
                filter.Module = Module;

                return filter;
            }
        }
    }
}
